Skip to content

fix(child-session): dynamically exclude blocked package tools from child inheritance (#483) - #494

Closed
naivezip wants to merge 1 commit into
openpi-dev:mainfrom
naivezip:develop
Closed

naivezip wants to merge 1 commit into
openpi-dev:mainfrom
naivezip:develop

Conversation

@naivezip

@naivezip naivezip commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Fixes #483. Also addresses the preflight root cause reported in #493.

When @tt-a1i/openpi is co-installed with third-party extensions such as pi-intercom, subagent_spawn (including standard subagents without explicit agent_type) and Workflow child sessions fail during startup preflight before the first model turn:

Child tool preflight failed: requested tool "intercom" is unavailable after child extensions initialized. Check the Agent Type tools list and child extension loading.

Root cause:
blockedPackageSources() strips pi-intercom from child session extensions/packages to avoid cross-wiring session identity (per #128). However, during child tool allowlist resolution, inheritedChildToolAllowlist(pi.getActiveTools(), roleTools) blindly inherited all active parent tools unless they were explicitly listed in OpenPI's internal CHILD_EXCLUDED_TOOL_NAMES. Because foreign tools like intercom do not belong in OpenPI's internal tool surface (and adding them there violates the bidirectional fail-closed drift guard), tools from blocked packages leaked into the child requested tool list where they could never be satisfied.

Value

  • Restores clean subagent delegation and workflow execution when third-party packages like pi-intercom are present in the parent environment.
  • Provides a structural alignment fix rather than hardcoding foreign tool names in OpenPI internal tool lists.
  • Dynamically respects package blocking boundaries established in #128 without breaking the bidirectional drift guard or requiring ongoing manual maintenance of third-party tool names.

Approach

  1. Dynamic Tool Provenance Filtering in child-session.ts:
    • Keep CHILD_EXCLUDED_TOOL_NAMES strictly scoped to OpenPI-owned parent tools, preserving the bidirectional partition invariant (CHILD_SAFE_PACKAGE_TOOL_NAMES + CHILD_EXCLUDED_TOOL_NAMES == OPENPI_TOOL_SURFACE_NAMES).
    • Add ChildToolInheritanceOptions ({ availableTools?: readonly PiToolDescriptor[]; cwd?: string }) and isBlockedChildTool(tool, options) to inspect tool provenance (tool.sourceInfo) against blocked child package matchers (createPiIntercomPackageMatcher).
    • In inheritedChildToolAllowlist, if available tool descriptors are provided, dynamically drop any tools originating from blocked child packages.
  2. Plumb Available Tools at Caller Seams:
    • In extensions/subagents/index.ts, pass { availableTools: pi.getAllTools?.(), cwd: ctx.cwd } into inheritedChildToolAllowlist.
    • In extensions/workflows/index.ts, pass { availableTools: pi.getAllTools?.(), cwd: ctx.cwd } into inheritedChildToolAllowlist.
  3. Drift Guard & Regression Testing:
    • Retain the untouched, fail-closed bidirectional drift guard in tests/extensions/shared/child-session.test.ts.
    • Add regression test asserting that tools from blocked packages (like intercom from pi-intercom) are dynamically excluded during inheritance and allow child sessions to pass preflight.
    • Revert any foreign tool workarounds from tests/extensions/shared/tool-surface.test.ts and tests/extensions/subagents/agent-types.test.ts.

Validation

  • node --test --experimental-strip-types tests/extensions/shared/child-session.test.ts: 20/20 passed
  • node --test --experimental-strip-types tests/extensions/shared/tool-surface.test.ts: 9/9 passed
  • node --test --experimental-strip-types tests/extensions/subagents/agent-types.test.ts: 22/22 passed
  • node --test --experimental-strip-types tests/extensions/subagents/index.test.ts: 16/16 passed
  • node --test --experimental-strip-types tests/extensions/subagents/pi-backend.test.ts: 5/5 passed
  • node --test --experimental-strip-types tests/extensions/workflows/runner.test.ts: 26/26 passed
  • node scripts/check-config-contract.mjs: 15/15 fields passed
  • node scripts/check-discipline-ledger.mjs: 12/12 rows passed
  • npx biome format & npx biome lint --error-on-warnings: Clean, 0 errors
  • git diff upstream/main..HEAD: Exactly 4 files changed (+183, -1)

Impact

  • User-visible behavior: Subagents and workflows start cleanly when pi-intercom is co-installed in the parent.
  • Model-visible context/tools: Tools from blocked child packages are omitted from child session tool allowlists.
  • Runtime/lifecycle: Preserves child isolation invariants from setup: 移除 OpenPI 对 pi-intercom 的主动安装与配置职责 #128 fail-closed.
  • Persistence/configuration: No configuration schema changes.
  • Compatibility & risks: Low risk; structural alignment between package filtering and tool inheritance boundaries.

@github-actions github-actions Bot added the area:subagents Subagent delegation, skills, or tests label Sep 8, 2026
@github-actions github-actions Bot added the area:workflows Workflow engine, capability, skills, or tests label Sep 9, 2026
@naivezip naivezip changed the title fix(child-session): exclude intercom from child tool allowlist (#483) fix(child-session): dynamically exclude blocked package tools from child inheritance (#483) Sep 9, 2026
…ild inheritance (openpi-dev#483)

pi-intercom is excluded from child sessions via blockedPackageSources to prevent concurrent process.env session cross-wiring (openpi-dev#128). However, when pi-intercom was active in the parent session, inheritedChildToolAllowlist projected its tools into child sessions, causing subagent_spawn and workflow child sessions to fail preflight checks.

Rather than hardcoding foreign tool names into OpenPI's CHILD_EXCLUDED_TOOL_NAMES (which is strictly reserved for OpenPI-owned parent-only tools and enforced by the fail-closed drift guard), dynamically drop tools from blocked packages during child inheritance:

- Inspect tool.sourceInfo in inheritedChildToolAllowlist against blocked child package matchers
- Pass pi.getAllTools() and cwd context from subagents/index.ts and workflows/index.ts
- Preserve CHILD_EXCLUDED_TOOL_NAMES and bidirectional drift guards strictly for OpenPI tools
- Add regression coverage verifying dynamic exclusion of tools from blocked packages while preserving ordinary tools and passing child preflight

@tt-a1i tt-a1i left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at exact head 88531a1.

Standards

[P1] Unverifiable tool provenance currently fails open. The new local descriptor makes sourceInfo and availableTools optional, and isBlockedChildTool() returns false when package identity inspection throws. A blocked-package tool with missing or unreadable provenance is therefore treated as child-safe. Pi 0.85.1 provides getAllTools() with sourceInfo; please use that authoritative type non-optionally and deny/throw when non-builtin provenance cannot be verified.

[P2] Blocked package identity is implemented twice with different conditions: blockedPackageSources() filters canonical resources, while isBlockedChildTool() independently hardcodes pi-intercom matching and ignores origin. Extract one shared blocked-package identity policy for both projections.

Spec

[P2] The regression covers a helper and synthetic preflight only. It does not execute Direct spawn, typed spawn, or Workflow callers with a real local/npm/git pi-intercom fixture, so omission or caller-plumbing drift would go undetected. Because this changes the child model-visible authority boundary, please add caller-level Direct and Workflow tests.

[P3] The array shorthand and unused agentDir generality are outside the required production path.

@JS-banana

Copy link
Copy Markdown
Contributor

A follow-up implementation is available in #528, rebased onto current main. It retains naivezip's original commit and attribution, followed by a separate corrective commit from JS-banana; this PR's branch is unchanged.

The correction uses required Pi tool provenance, shares the loaded-resource filtering policy, removes the catch-and-guess fallback, and resolves nested single-file local sources through Pi's scoped installed-path lookup. It also adds real-parent-metadata Direct/typed Direct and Workflow caller regressions.

The extended package regression fails at this PR's current head (88531a1) and passes with the correction. On #528: bun run check passed; bun run test passed with Node 1527 passed / 1 skipped and Vitest 130 passed. Live provider/interactive Pi acceptance remains unverified.

#528 includes the original change plus the corrections, so maintainers can review the combined result there without needing to merge both PRs independently. Please preserve both authors' attribution if squashing.

@tt-a1i tt-a1i left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

审查提交:88531a124cd6f8fa129368f842f66796a9468ad3。

需要修改后再合并。

具体问题

P2 — 来源无法验证时仍保留请求工具,阻止子 Session 的正常启动 · extensions/shared/child-session.ts:585

sourceInfo/availableTools optional and inspection catch returns false for opaque local paths. This retains excluded names; subsequent bindChildSessionExtensions missing-tool check rejects startup. Use authoritative nonoptional Pi ToolInfo and explicit provenance failure; current tests only mock descriptors.

验证范围

Full diff and Pi 0.85.1 getAllTools source metadata/preflight inspected; 20 helper tests pass. Existing review concerns still unaddressed at same head.

限制与后续

Do not claim authority escalation: child resource loader and preflight remain fail-closed. No actual local/npm/git package integration reproduction performed.

@naivezip

naivezip commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

Closing as superseded: this change landed on main via #528 (squash commit 81bfd34), which kept this PR's commit and preserved authorship (Co-authored-by: naivezip) — issues #483 and #493 were closed by that merge. The provenance-based filtering plus regression coverage are in main, so this branch is no longer needed (it had also drifted into a conflict with main). Thanks @JS-banana for carrying the correction and the maintainers for keeping attribution.

@naivezip naivezip closed this Oct 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:subagents Subagent delegation, skills, or tests area:workflows Workflow engine, capability, skills, or tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(child-session): pi-intercom 同装时 subagent_spawn 预检必挂——intercom 未进 CHILD_EXCLUDED_TOOL_NAMES

3 participants